#include "myFunctions.h"

using namespace std;

/**
Lee de un archivo valores de varias entradas y una salida separados por comas
*/
void readFile(mdouble&X,vector<vector<int> >& Y, string file, bool insX0){
	ifstream archi;
	archi.open(file.c_str());
	if(archi.is_open()){
		while(!archi.eof()){
			string str_line;
			getline(archi,str_line);
			//char* line;
			//line = &str_line[0];
			if(!archi.eof())
				explode(str_line, X, Y, insX0);
			//explode(line, X, Y);
		}
	archi.close();
	}
}


/**
Dado un string, separa su contenido, separado por comas,
en valores numericos y los agrega a los vectores pasados por referencia
*/
void explode(string line, mdouble&X, vector<vector<int> >&Y, bool insX0){
	vector<double>Aux;
	vector<int> yAux;
	if(insX0)
		Aux.push_back(-1); //valor que prop movilidad a la recta(abcisa)
	char Buffer[25]; //maximo 25 cifras
	for(int l=0;l<25;l++) Buffer[l] = '\0';
	int i = 0;
	for (int k=0; line[k] != '\0'; k++){
		if (line[k] != ','){
			Buffer[i] = line[k];
			i++;
		}else{
			Aux.push_back(atof(Buffer));
			for(int l=0;l<25;l++) Buffer[l] = '\0';
			i = 0;
		}
	}
	yAux.push_back(atoi(Buffer));
	Y.push_back(yAux); //el ultimo que lei es la salida	
	X.push_back(Aux);	   //agrego las nuevas entradas	
}


/**
Lee de un archivo valores de varias entradas y una salida separados por comas
*/
void readFile(mdouble&X, vector<int>&Y, string file, bool insX0){
	ifstream archi;
	archi.open(file.c_str());
	if(archi.is_open()){
		while(!archi.eof()){
			string str_line;
			getline(archi,str_line);
			//char* line;
			//line = &str_line[0];
			if(!archi.eof())
				explode(str_line, X, Y, insX0);
			//explode(line, X, Y);
		}
	archi.close();
	}
}


/**
Dado un string, separa su contenido, separado por comas,
en valores numericos y los agrega a los vectores pasados por referencia
*/
void explode(string line, mdouble&X, vector<int>&Y, bool insX0){
	vector<double>Aux;
	if(insX0)
		Aux.push_back(-1); //valor que prop movilidad a la recta(abcisa)
	char Buffer[25]; //maximo 25 cifras
	for(int l=0;l<25;l++) Buffer[l] = '\0';
	int i = 0;
	for (int k=0; line[k] != '\0'; k++){
		if (line[k] != ','){
			Buffer[i] = line[k];
			i++;
		}else{
			Aux.push_back(atof(Buffer));
			for(int l=0;l<25;l++) Buffer[l] = '\0';
			i = 0;
		}
	}
	Y.push_back(atoi(Buffer)); //el ultimo que lei es la salida	
	X.push_back(Aux);	   //agrego las nuevas entradas	
}

/**
Guarda en un archivo, datos separados por comas
*/
void saveFile(mdouble&X, vector<int>&y, string file){
	ofstream archi(file.c_str(), ios::trunc);
	unsigned int nRow = X.size();
	unsigned int nCol = X[0].size();
	if(archi.is_open()){	
		for (unsigned int i=0;i<nRow;i++){ //habria que mejorar como lee el tamaño, por ahora funca
			for (unsigned int j=0;j<nCol;j++){
				archi<<X[i][j]<<",";
			}
			archi<<y[i]<<endl;
		}
		archi.close();
	}
}

/**
Guarda en un archivo, datos separados por comas, solo 1 vector
*/
void saveFileX(mdouble& X, string file){
	ofstream archi(file.c_str(), ios::trunc);
	unsigned int nRow = X.size();
	unsigned int nCol = X[0].size();
	if(archi.is_open()){	
		for (unsigned int i=0;i<nRow;i++){ //habria que mejorar como lee el tamaño, por ahora funca
			for (unsigned int j=0;j<nCol;j++){
				if(j<(nCol-1))
					archi<<X[i][j]<<" ";
				else
					archi<<X[i][j]<<endl;
			}
		}
		archi.close();
	}
}
/**
Dado un archivo con unos pocos valores de entradas y salida, genera un nuevo archivo
de "n" filas, con una desviacion "desv" en torno a los primeros
*/
void genRandom(string file, string new_file, double desv, int n){
	mdouble X,Xgen;
	vector<int> y, ygen;
	readFile(X,y,file,false);
	int nRow = X.size();
	int nCol = X[0].size(); 
	int i;
	double insert;
	for(int j=0; j<n; j++){ 
		vector<double> Aux;
		i = int(rand() % nRow);
		for(int k=0;k<nCol;k++){
			double percentage = double(rand() % int(2*desv) - desv);
			insert = X[i][k] +  X[i][k]*percentage/100;
			Aux.push_back(insert);
		}
		Xgen.push_back(Aux);
		ygen.push_back(y[i]);
	}
	saveFile(Xgen, ygen, new_file);
}

/**
Con la matriz de entradas y el vector de salidas genera por ahora 20 particiones
de los datos, seleccionando porcTraining% de los datos para entrenamiento y el resto
para pruebas. Los guarda en archivos separados.
*/
void particionado(mdouble&X, vector<int>&Y, double porcTraining, int numpart){
	mdouble xAux,xAuxTest,X1;	//Los Aux son los vectores donde se van formando las nuevas particiones
	vector<int> yAux,yAuxTest,Y1;
	int tamanio = X.size();
	vector<vector<double> >::iterator it1;	//iterador para recorrer X1
	vector<int>::iterator it2;	//iteradores de los vectores y e yAux
	xAux.resize(int(X.size()*porcTraining)+1);
	yAux.resize(int(Y.size()*porcTraining)+1);
	
	for(int i=0; i<numpart; i++){	//Va a realizar numpart distintas particiones
		X1=X;	//Copio los datos de X en X1 para no modificar X y trabajar con X1 dentro de cada iteración
		Y1=Y;	//Lo mismo que para X
		int indice=0;
		
		while(indice <= (tamanio*porcTraining)){	
			srand(time(NULL));
			unsigned int azar = abs(rand() % X1.size());
			xAux[indice].resize(X[azar].size());

			copy(X1[azar].begin(), X1[azar].end(),xAux[indice].begin());
			yAux[indice] = Y1[azar];
			
			X1.erase(X1.begin()+azar);	//Borra la fila	para que en la proxima iteración no pueda ser elegida nuevamente
			Y1.erase(Y1.begin()+azar);
			indice++;
		}
		xAuxTest.resize(X1.size());
		yAuxTest.resize(Y1.size());
		//Copia los datos que quedaron sin elegir para ser usados para prueba.
		copy(X1.begin(), X1.end(),xAuxTest.begin());
		copy(Y1.begin(), Y1.end(),yAuxTest.begin());

		stringstream cvz;
		cvz<<i;
		string snumero=cvz.str();
		string nomArchi="Training/partTraining"+snumero+".txt";
		saveFile(xAux,yAux,nomArchi);	//Guarda el archivo con la partición de entrenamiento realizada
		nomArchi="Test/partTest"+snumero+".txt";
		saveFile(xAuxTest,yAuxTest,nomArchi);
		cout<<"Generando particionado..."<<endl;
	}
}

void loadConfig(int& numPatterns, int& numParticiones, double& porcTT, double& desvioDatos){
	ifstream archi;
	archi.open("config.txt");
	if(archi.is_open()){
		string str_line;
		char* line;

		getline(archi,str_line);
		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		numPatterns = atoi(line);

		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		numParticiones = atoi(line);

		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		porcTT = atof(line);

		getline(archi,str_line);
		getline(archi,str_line);
		line = &str_line[0];
		desvioDatos = atof(line);

		archi.close();
	}

}


//Funcion signo
int sign(double x){
	return ((fabs(x)-x) ? -1 : 1); 
}

/*Random normalizado [0;1]*/
double normRandom(){
  double value = ((double)rand() / ((double)(RAND_MAX)+(double)(1)) );
	return value;
}




///----------------------------------------------------------------------------------///
///                          FUNCIONES ALGEBRAICAS                                   ///

/**
Resuelve el producto punto x.w = y
*/
double prod_punto(vector<double> &x, vector<double> &w){
	int l = x.size();
	double y = 0;	
	for(int i=0;i<l;i++){
		y += x[i]*w[i];		
	}
	return y;
}

/**
Resuelve el producto matricial X w = y
*/
vector<double> prod_mat(mdouble &X, vector<double> &w){
	int l1 = X.size();
	vector<double> aux;
	aux.resize(l1); 
	for(int i=0;i<l1;i++){
		aux[i] = prod_punto(X[i],w);		
	}
	return aux;
}


double euclideanDist(int n,vector<double>& x,vector<double>& y){
	double dist = 0;
	for(int i=0; i<n; i++){
		dist += (x[i]-y[i])*(x[i]-y[i]);
	}
	return sqrt(dist);
}


double difference(vector<double>& x ,vector<double>& y, int power = 1){
	double res = 0;
	int l = x.size();
	for(int i=0;i<l;i++){
		res += pow((x[i]-y[i]), power);	
	}
	return res;
}

vector<double> inverse_diagonal(vector<double>& x){
	vector<double> res;
	int l = x.size();
	res.resize(l);
	for(int i=0;i<l;i++){
		res[i] = 1/x[i];	
	}
	return res;
}

vector<double> prod_vec_mat_diag(vector<double>& v, vector<double>& m){
	vector<double> res;
	int l = v.size();
	res.resize(l);
	for(int i=0;i<l;i++){
		res[i] = v[i] * m[i];	
	}
	return res;
}

double norma_vec(vector<double>& x){
	double res = 0;
	int l = x.size();
	for(int i=0;i<l;i++){
		res += x[i] * x[i];	
	}
	return sqrt(res);
}

double media(vector<double>& x){
	double res = 0;
	int l = x.size();
	for(int i=0;i<l;i++){
		res += x[i];	
	}
	return res/l;
}

void view_vd(vector<double>& v){
	for (unsigned int i=0;i<v.size();i++){
		cout<<v[i]<<" - ";	
	}
	cout<<endl;
}

void view_vi(vector<int>& v){
	for (unsigned int i=0;i<v.size();i++){
		cout<<v[i]<<" - ";	
	}
	cout<<endl;
}

double desvio (vector<double> &V){
	double mean = media(V),desv = 0;
	int tam = V.size();
	for(int i=0;i<tam;i++){
		desv += (V[i]-mean)*(V[i]-mean);
	}
	return desv/tam;
}
